/*
 * Copyright (C) 2017 skydoves
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.skydoves.powermenu;

import com.skydoves.powermenu.shape.ShapeUtil;
import ohos.aafwk.ability.ILifecycle;
import ohos.aafwk.ability.Lifecycle;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.ListContainer;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.biometrics.authentication.IFaceAuthentication;

import java.util.ArrayList;
import java.util.List;

/**
 * CustomPowerMenu is one the implementation of the {@link AbstractPowerMenu}.
 *
 * <p>It implements the customized {@link PowerMenu} by the user.
 */
public class CustomPowerMenu<T, E extends MenuBaseAdapter<T>> extends AbstractPowerMenu<T, E> {
    private DependentLayout binding;
    private DependentLayout materialBinding;

    protected CustomPowerMenu(
            Context context, AbstractMenuBuilder abstractMenuBuilder) {
        super(context, abstractMenuBuilder, null);
        Builder<T, E> builder = (Builder<T, E>) abstractMenuBuilder;
        if (builder.menuItemClickListener != null) {
            setOnMenuItemClickListener(builder.menuItemClickListener);
        }
        if (builder.selected != -1) {
            setSelectedPosition(builder.selected);
        }
        if (builder.headerView != null) {
            builder.headerView.setBackground(ShapeUtil.createRectangleCornersDrawable(
                    (ShapeElement) builder.headerView.getBackgroundElement(),
                    builder.menuRadius, builder.menuRadius,
                    0, 0));
            menuHeaderView.addComponent(builder.headerView);
        }
        if (builder.footerView != null) {
            menuFooterView.addComponent(builder.footerView, new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));
        }
        if (builder.alignment != LayoutAlignment.UNSET) {
            ComponentContainer.LayoutConfig layoutConfig = menuView.getLayoutConfig();
            ((DependentLayout.LayoutConfig) layoutConfig).addRule(DependentLayout.LayoutConfig.CENTER_IN_PARENT);
            menuView.setLayoutConfig(layoutConfig);
        }
        this.adapter = builder.adapter;
        this.adapter.setListView(getMenuListView());
        addItemList(builder.Ts);
        this.menuListView.setItemProvider(adapter);
        if (builder.headerView != null || builder.footerView != null) {
            ComponentContainer.LayoutConfig layoutConfig = menuListView.getLayoutConfig();
            layoutConfig.height = ComponentContainer.LayoutConfig.MATCH_CONTENT;
            layoutConfig.width = ComponentContainer.LayoutConfig.MATCH_PARENT;
            menuListView.setLayoutConfig(layoutConfig);
        }
        if (menuWindow instanceof CommonDialog) {
            ((CommonDialog) menuWindow).setContentCustomComponent(backgroundView);
        }
        getAdapter().setItemClickListener(itemClickListener);
    }

    @Override
    protected void initialize(Context context, Boolean isMaterial, Component component) {
        LayoutScatter layoutInflater = LayoutScatter.getInstance(context);
        if (isMaterial) {
            materialBinding =
                    (DependentLayout) layoutInflater.parse(ResourceTable.Layout_layout_material_power_menu_library_skydoves, null, false);
        } else {
            binding = (DependentLayout) layoutInflater.parse(ResourceTable.Layout_layout_power_menu_library_skydoves, null, false);
        }
        this.adapter = (E) (new MenuBaseAdapter<>(menuListView));
        super.initialize(context, isMaterial, component);
    }

    @Override
    Component getMenuRoot(Boolean isMaterial) {
        if (isMaterial) {
            return materialBinding.findComponentById(ResourceTable.Id_power_menu_layout);
        } else {
            return binding.findComponentById(ResourceTable.Id_power_menu_layout);
        }
    }

    @Override
    DependentLayout getMenuBackground(Boolean isMaterial) {
        if (isMaterial) {
            return materialBinding;
        } else {
            return binding;
        }
    }

    DirectionalLayout getMenuHeaderView(Boolean isMaterial) {
        if (isMaterial) {
            return (DirectionalLayout) materialBinding.findComponentById(ResourceTable.Id_head);
        } else {
            return (DirectionalLayout) binding.findComponentById(ResourceTable.Id_head);
        }
    }

    DirectionalLayout getMenuFooterView(Boolean isMaterial) {
        if (isMaterial) {
            return (DirectionalLayout) materialBinding.findComponentById(ResourceTable.Id_foot);
        } else {
            return (DirectionalLayout) binding.findComponentById(ResourceTable.Id_foot);
        }
    }

    @Override
    ListContainer getMenuList(Boolean isMaterial) {
        if (isMaterial) {
            return (ListContainer) materialBinding.findComponentById(ResourceTable.Id_power_menu_listView);
        } else {
            return (ListContainer) binding.findComponentById(ResourceTable.Id_power_menu_listView);
        }
    }

    @Override
    public void onStateChanged(Lifecycle.Event event, Intent intent) {
    }

    @Override
    public void setItemClickListener(ListContainer.ItemClickedListener itemClickListener) {
    }

    /**
     * Builder class for creating {@link CustomPowerMenu}.
     */
    public static class Builder<T, E extends MenuBaseAdapter<T>> extends AbstractMenuBuilder {
        private OnMenuItemClickListener<T> menuItemClickListener = null;

        private final E adapter;
        private final List<T> Ts;
        private int alignment = LayoutAlignment.UNSET;

        public Builder(Context context, E adapter) {
            this.context = context;
            this.Ts = new ArrayList<>();
            this.adapter = adapter;
            this.layoutInflater = LayoutScatter.getInstance(context);
        }

        /**
         * sets the {@link ILifecycle} for dismissing automatically when the {@link ILifecycle}
         * is destroyed. It will prevents memory leak.
         *
         * @param iLifecycle lifecycle owner
         * @return {@link Builder}.
         * @see <a href="https://github.com/skydoves/PowerMenu#avoid-memory-leak">GitHub :
         * PowerMenu-Avoid-Memory-Leak</a>
         */
        public Builder<T, E> setILifecycle(ILifecycle iLifecycle) {
            this.iLifecycle = iLifecycle;
            return this;
        }

        /**
         * sets the visibility of the background popup.
         *
         * @param show visibility of the background popup.
         * @return {@link Builder}.
         */
        public Builder<T, E> setShowBackground(boolean show) {
            this.showBackground = show;
            return this;
        }

        /**
         * sets the {@link OnMenuItemClickListener} to the popup.
         *
         * @param menuItemClickListener {@link OnMenuItemClickListener} interface.
         * @return {@link Builder}.
         */
        public Builder<T, E> setOnMenuItemClickListener(Object menuItemClickListener) {
            this.menuItemClickListener = (OnMenuItemClickListener<T>) menuItemClickListener;
            return this;
        }

        /**
         * sets the {@link Component.ClickedListener} to the popup.
         *
         * @param onBackgroundClickListener {@link Component.ClickedListener} interface.
         * @return {@link Builder}.
         */
        public Builder<T, E> setOnBackgroundClickListener(
                Component.ClickedListener onBackgroundClickListener) {
            this.backgroundClickListener = onBackgroundClickListener;
            return this;
        }

        /**
         * sets the {@link OnDismissedListener} to the popup.
         *
         * @param onDismissListener {@link OnDismissedListener} interface.
         * @return {@link Builder}.
         */
        public Builder<T, E> setOnDismissListener(OnDismissedListener onDismissListener) {
            this.onDismissedListener = onDismissListener;
            return this;
        }

        /**
         * sets the header view by layout resource.
         *
         * @param headerView layout resource
         * @return {@link Builder}.
         */
        public Builder<T, E> setHeaderView(int headerView) {
            this.headerView = layoutInflater.parse(headerView, null, false);
            return this;
        }

        /**
         * sets the header view by layout view.
         *
         * @param headerView header view.
         * @return {@link Builder}.
         */
        public Builder<T, E> setHeaderView(Component headerView) {
            this.headerView = headerView;
            return this;
        }

        /**
         * sets the footer view by layout resource.
         *
         * @param footerView footer view.
         * @return {@link Builder}.
         */
        public Builder<T, E> setFooterView(int footerView) {
            this.footerView = layoutInflater.parse(footerView, null, false);
            return this;
        }

        /**
         * sets the footer view by layout view.
         *
         * @param footerView footer view.
         * @return {@link Builder}.
         */
        public Builder<T, E> setFooterView(Component footerView) {
            this.footerView = footerView;
            return this;
        }

        /**
         * sets the menu animation to the popup.
         *
         * @param menuAnimation animation.
         * @return {@link Builder}.
         */
        public Builder<T, E> setAnimation(MenuAnimation menuAnimation) {
            this.menuAnimation = menuAnimation;
            return this;
        }

        /**
         * sets the customized menu animation using resource.
         *
         * @param style animation resource.
         * @return {@link Builder}.
         */
        public Builder<T, E> setAnimationStyle(int style) {
            this.animationStyle = style;
            return this;
        }

        /**
         * sets the corner radius of the popup menu.
         *
         * @param radius corner radius.
         * @return {@link Builder}.
         */
        public Builder<T, E> setMenuRadius(float radius) {
            this.menuRadius = radius;
            return this;
        }

        /**
         * sets the shadow of the popup menu.
         *
         * @param shadow popup shadow.
         * @return {@link Builder}.
         */
        public Builder<T, E> setMenuShadow(float shadow) {
            this.menuShadow = shadow;
            return this;
        }

        /**
         * sets the width size of the popup menu.
         *
         * @param width width size.
         * @return {@link Builder}.
         */
        public Builder<T, E> setWidth(int width) {
            this.width = width;
            return this;
        }

        /**
         * sets the height size of the popup menu.
         *
         * @param height height size.
         * @return {@link Builder}.
         */
        public Builder<T, E> setHeight(int height) {
            this.height = height;
            return this;
        }

        /**
         * sets a padding size of the popup menu.
         *
         * @param padding padding size.
         * @return {@link PowerMenu.Builder}
         */
        public Builder<T, E> setPadding(int padding) {
            this.padding = padding;
            return this;
        }

        /**
         * sets the width and height size of the popup menu.
         *
         * @param width  width size.
         * @param height height size.
         * @return {@link Builder}.
         */
        public Builder<T, E> setSize(int width, int height) {
            this.width = width;
            this.height = height;
            return this;
        }

        /**
         * sets the divider height between the menu items.
         *
         * @param height divider height between the menu items.
         * @return {@link Builder}.
         */
        public Builder<T, E> setDividerHeight(int height) {
            this.dividerHeight = height;
            return this;
        }

        /**
         * sets the color of the background popup.
         *
         * @param color color of the background popup.
         * @return {@link Builder}.
         */
        public Builder<T, E> setBackgroundColor(int color) {
            this.backgroundColor = color;
            return this;
        }

        /**
         * sets the color of the background popup.
         *
         * @param color color of the background popup by resource.
         * @return {@link Builder}.
         */
        public Builder<T, E> setBackgroundColorResource(int color) {
            this.backgroundColor = context.getColor(color);
            return this;
        }

        /**
         * sets the aligment of the background popup.
         *
         * @param aligment aligment of the background popup.
         * @return {@link Builder}.
         */
        public Builder setMenuAligment(int aligment) {
            this.alignment = aligment;
            return this;
        }

        /**
         * sets the focusability of the popup menu.
         *
         * @param focusable focusability of the popup menu.
         * @return {@link Builder}.
         */
        public Builder<T, E> setFocusable(boolean focusable) {
            this.focusable = focusable;
            return this;
        }

        /**
         * sets the initialized selected effect menu item position.
         *
         * @param position initialized selected effect menu item position.
         * @return {@link Builder}.
         */
        public Builder<T, E> setSelected(int position) {
            this.selected = position;
            return this;
        }

        /**
         * sets the clipping or not of the popup menu.
         *
         * @param isClipping clipping or not of the popup menu.
         * @return {@link Builder}.
         */
        public Builder<T, E> setIsClipping(boolean isClipping) {
            this.isClipping = isClipping;
            return this;
        }

        /**
         * sets the dismiss action if already popup is showing.
         *
         * <p>Recommend to use with setFocusable(true) and setShowBackground(false).
         *
         * @param dismissIfShowAgain dismiss if already popup is showing.
         * @return Builder
         */
        public Builder<T, E> setDismissIfShowAgain(boolean dismissIfShowAgain) {
            this.dismissIfShowAgain = dismissIfShowAgain;
            return this;
        }

        /**
         * sets the dismissing automatically when the menu item is clicked.
         *
         * @param autoDismiss dismissing automatically when the menu item is clicked.
         * @return {@link Builder}.
         */
        public Builder<T, E> setAutoDismiss(boolean autoDismiss) {
            this.autoDismiss = autoDismiss;
            return this;
        }

        /**
         * adds an {@link PowerMenuItem} item to the popup menu list.
         *
         * @param item {@link PowerMenuItem} item.
         * @return {@link Builder}.
         */
        public Builder<T, E> addItem(Object item) {
            this.Ts.add((T) item);
            return this;
        }

        /**
         * adds an {@link PowerMenuItem} item to the popup menu list at position.
         *
         * @param position specific position.
         * @param item     {@link PowerMenuItem} item.
         * @return {@link Builder}.
         */
        public Builder<T, E> addItem(int position, Object item) {
            this.Ts.add(position, (T) item);
            return this;
        }

        /**
         * adds a list of the {@link PowerMenuItem} item to the popup menu list.
         *
         * @param itemList list of the {@link PowerMenuItem}.
         * @return {@link Builder}.
         */
        public Builder<T, E> addItemList(List<T> itemList) {
            this.Ts.addAll(itemList);
            return this;
        }

        /**
         * sets the preference name of the popup menu. For persistence the clicked item position. If the
         * preference name is set, the persisted position item will be clicked automatically when the
         * popup menu initialized. This option should be used with setInitializeRule and
         * setILifecycle.
         *
         * @param preferenceName name of the popup menu.
         * @return {@link Builder}.
         * @see <a href="https://github.com/skydoves/PowerMenu#preference"</a>
         */
        public Builder<T, E> setPreferenceName(String preferenceName) {
            this.preferenceName = preferenceName;
            return this;
        }

        /**
         * sets the initialization rule of the recovering persisted position.
         *
         * @param event           when should be recovered.
         * @param defaultPosition default selected position.
         * @return {@link Builder}.
         */
        public Builder<T, E> setInitializeRule(Lifecycle.Event event, int defaultPosition) {
            this.initializeRule = event;
            this.defaultPosition = defaultPosition;
            return this;
        }

        /**
         * sets the circular revealed effect using {@link CircularEffect}.
         *
         * @param circularEffect circular revealed effect using {@link CircularEffect}.
         * @return @return {@link Builder}.
         */
        public Builder<T, E> setCircularEffect(CircularEffect circularEffect) {
            this.circularEffect = circularEffect;
            return this;
        }

        /**
         * sets the menu layout should be composed of material components.
         *
         * @param isMaterial is material layout or not.
         * @return @return {@link PowerMenu.Builder}.
         */
        public Builder<T, E> setIsMaterial(Boolean isMaterial) {
            this.isMaterial = isMaterial;
            return this;
        }

        public CustomPowerMenu<T, E> build() {
            return new CustomPowerMenu<>(context, this);
        }
    }

    /**
     * An abstract factory class for creating an instance of {@link CustomPowerMenu}.
     *
     * <p>A factory implementation class must have a non-argument constructor.
     */
    public abstract static class Factory<T, E extends MenuBaseAdapter<T>> {
        /**
         * CustomPowerMenu instance
         *
         * @param context   context
         * @param lifecycle lifecycle
         * @return PowerMenu
         */
        public abstract CustomPowerMenu<T, E> create(
                Context context, ILifecycle lifecycle);
    }
}
